Apgūstiet reāllaika ēnu renderēšanas pamatus un progresīvas tehnikas WebGL. Ceļvedis aptver ēnu kartēšanu, PCF, CSM un biežāko artefaktu risinājumus.
WebGL ēnu kartēšana: visaptverošs ceļvedis reāllaika renderēšanā
3D datorgrafikas pasaulē daži elementi veicina reālismu un imersiju vairāk nekā ēnas. Tās sniedz būtiskas vizuālas norādes par telpiskajām attiecībām starp objektiem, gaismas avotu atrašanās vietu un ainas kopējo ģeometriju. Bez ēnām 3D pasaules var šķist plakanas, nesavienotas un mākslīgas. Web bāzētām 3D lietojumprogrammām, kuras darbina WebGL, augstas kvalitātes reāllaika ēnu ieviešana ir profesionāla līmeņa pieredzes pazīme. Šis ceļvedis sniedz dziļu ieskatu fundamentālākajā un plašāk izmantotajā tehnikā, lai to sasniegtu: ēnu kartēšanā (Shadow Mapping).
Neatkarīgi no tā, vai esat pieredzējis grafikas programmētājs vai tīmekļa izstrādātājs, kas uzsāk darbu trešajā dimensijā, šis raksts sniegs jums zināšanas, lai saprastu, ieviestu un novērstu problēmas ar reāllaika ēnām jūsu WebGL projektos. Mēs dosimies ceļojumā no pamatteorijas līdz praktiskās ieviešanas detaļām, izpētot biežākās kļūdas un progresīvās tehnikas, kas tiek izmantotas mūsdienu grafikas dzinējos.
1. nodaļa: Ēnu kartēšanas pamati
Savā būtībā ēnu kartēšana ir gudra un eleganta tehnika, kas nosaka, vai punkts ainā atrodas ēnā, uzdodot vienkāršu jautājumu: "Vai šo punktu var redzēt no gaismas avota?" Ja atbilde ir nē, tas nozīmē, ka kaut kas bloķē gaismu, un punktam jābūt ēnā. Lai uz šo jautājumu atbildētu programmatiski, mēs izmantojam divu piegājienu renderēšanas metodi.
Kas ir ēnu kartēšana? Pamatkoncepcija
Visa tehnika balstās uz ainas renderēšanu divreiz, katru reizi no cita skatu punkta:
- 1. piegājiens: dziļuma piegājiens (no gaismas avota perspektīvas). Vispirms mēs renderējam visu ainu no precīzas gaismas avota pozīcijas un orientācijas. Tomēr šajā piegājienā mums nerūp krāsas vai tekstūras. Vienīgā informācija, kas mums nepieciešama, ir dziļums. Katram renderētajam objektam mēs ierakstām tā attālumu no gaismas avota. Šis dziļuma vērtību apkopojums tiek saglabāts īpašā tekstūrā, ko sauc par ēnu karti vai dziļuma karti. Katrs pikselis šajā kartē attēlo attālumu līdz tuvākajam objektam no gaismas skatu punkta noteiktā virzienā.
- 2. piegājiens: ainas piegājiens (no kameras perspektīvas). Pēc tam mēs renderējam ainu kā parasti, no galvenās kameras perspektīvas. Bet katram zīmētajam pikselim mēs veicam papildu aprēķinu. Mēs nosakām šī pikseļa pozīciju 3D telpā un pēc tam jautājam: "Cik tālu šis punkts ir no gaismas avota?" Pēc tam mēs salīdzinām šo attālumu ar vērtību, kas saglabāta mūsu ēnu kartē (no 1. piegājiena) atbilstošajā vietā.
Loģika ir vienkārša:
- Ja pikseļa pašreizējais attālums no gaismas ir lielāks par ēnu kartē saglabāto attālumu, tas nozīmē, ka tajā pašā redzes līnijā ir cits objekts, kas atrodas tuvāk gaismai. Tāpēc pašreizējais pikselis ir ēnā.
- Ja pikseļa attālums ir mazāks vai vienāds ar attālumu ēnu kartē, tas nozīmē, ka nekas to nebloķē, un pikselis ir pilnībā izgaismots.
Ainas sagatavošana
Lai ieviestu ēnu kartēšanu WebGL, jums ir nepieciešamas vairākas galvenās sastāvdaļas:
- Gaismas avots: Tas var būt virziena gaisma (piemēram, saule), punktveida gaisma (piemēram, spuldze) vai prožektors. Gaismas veids noteiks, kāda veida projekcijas matrica tiks izmantota dziļuma piegājienā.
- Kadru bufera objekts (FBO): WebGL parasti renderē uz ekrāna noklusējuma kadru buferi. Lai izveidotu mūsu ēnu karti, mums ir nepieciešams ārpus ekrāna renderēšanas mērķis. FBO ļauj mums renderēt tekstūrā, nevis uz ekrāna. Mūsu FBO tiks konfigurēts ar dziļuma tekstūras piesaisti.
- Divi ēnotāju komplekti: Jums būs nepieciešama viena ēnotāju programma dziļuma piegājienam (ļoti vienkārša) un otra gala ainas piegājienam (kas saturēs ēnu aprēķinu loģiku).
- Matricas: Jums būs nepieciešamas standarta modeļa, skata un projekcijas matricas kamerai. Būtiski, ka jums būs nepieciešama arī skata un projekcijas matrica gaismas avotam, kas bieži tiek apvienota vienā "gaismas telpas matricā".
2. nodaļa: divu piegājienu renderēšanas konveijers detalizēti
Aplūkosim abus renderēšanas piegājienus soli pa solim, koncentrējoties uz matricu un ēnotāju lomām.
1. piegājiens: dziļuma piegājiens (no gaismas avota perspektīvas)
Šī piegājiena mērķis ir aizpildīt mūsu dziļuma tekstūru. Lūk, kā tas darbojas:
- Piesaistīt FBO: Pirms zīmēšanas jūs norādāt WebGL renderēt uz savu pielāgoto FBO, nevis uz audekla (canvas).
- Konfigurēt skatvietu (Viewport): Iestatiet skatvietas izmērus, lai tie atbilstu jūsu ēnu kartes tekstūras izmēram (piem., 1024x1024 pikseļi).
- Notīrīt dziļuma buferi: Pārliecinieties, ka FBO dziļuma buferis ir notīrīts pirms renderēšanas.
- Izveidot gaismas avota matricas:
- Gaismas skata matrica: Šī matrica pārveido pasauli gaismas skatu punktā. Virziena gaismai to parasti izveido ar `lookAt` funkciju, kur "acs" ir gaismas pozīcija un "mērķis" ir virziens, kurā tā rāda.
- Gaismas projekcijas matrica: Virziena gaismai, kurai ir paralēli stari, tiek izmantota ortogrāfiskā projekcija. Punktveida gaismām vai prožektoriem tiek izmantota perspektīvā projekcija. Šī matrica definē telpas apjomu (kasti vai noplūdes piramīdu), kas metīs ēnas.
- Izmantot dziļuma ēnotāja programmu: Šis ir minimāls ēnotājs. Virsotņu ēnotāja vienīgais uzdevums ir reizināt virsotnes pozīciju ar gaismas skata un projekcijas matricām. Fragmentu ēnotājs ir vēl vienkāršāks: tas tikai ieraksta fragmenta dziļuma vērtību (tā z-koordinātu) dziļuma tekstūrā. Mūsdienu WebGL bieži vien nav nepieciešams pat pielāgots fragmentu ēnotājs, jo FBO var konfigurēt tā, lai tas automātiski tvertu dziļuma buferi.
- Renderēt ainu: Uzzīmējiet visus ēnas metošos objektus savā ainā. FBO tagad satur mūsu pabeigto ēnu karti.
2. piegājiens: ainas piegājiens (no kameras perspektīvas)
Tagad mēs renderējam gala attēlu, izmantojot tikko izveidoto ēnu karti, lai noteiktu ēnas.
- Atvienot FBO: Pārslēdzieties atpakaļ uz renderēšanu noklusējuma audekla kadru buferī.
- Konfigurēt skatvietu: Iestatiet skatvietu atpakaļ uz audekla izmēriem.
- Notīrīt ekrānu: Notīriet audekla krāsu un dziļuma buferus.
- Izmantot ainas ēnotāja programmu: Šeit notiek maģija. Šis ēnotājs ir sarežģītāks.
- Virsotņu ēnotājs: Šim ēnotājam ir jāveic divas darbības. Pirmkārt, tas aprēķina gala virsotnes pozīciju, izmantojot kameras modeļa, skata un projekcijas matricas kā parasti. Otrkārt, tam ir arī jāaprēķina virsotnes pozīcija no gaismas perspektīvas, izmantojot gaismas telpas matricu no 1. piegājiena. Šī otrā koordināta tiek nodota fragmentu ēnotājam kā mainīgais (varying).
- Fragmentu ēnotājs: Šis ir ēnu loģikas kodols. Katram fragmentam:
- Saņemt interpolēto pozīciju gaismas telpā no virsotņu ēnotāja.
- Veikt perspektīvo dalīšanu šai koordinātai (dalīt x, y, z ar w). Tas to pārveido normalizētās ierīces koordinātās (NDC), diapazonā no -1 līdz 1.
- Pārveidot NDC tekstūras koordinātās (kas ir diapazonā no 0 līdz 1), lai mēs varētu nolasīt mūsu ēnu karti. Šī ir vienkārša mērogošanas un nobīdes operācija: `texCoord = ndc * 0.5 + 0.5;`.
- Izmantot šīs tekstūras koordinātas, lai nolasītu 1. piegājienā izveidoto ēnu kartes tekstūru. Tas mums dod `depthFromShadowMap`.
- Fragmenta pašreizējais dziļums no gaismas perspektīvas ir tā z-komponents no pārveidotās gaismas telpas koordinātas. Sauksim to par `currentDepth`.
- Salīdzināt dziļumus: Ja `currentDepth > depthFromShadowMap`, fragments ir ēnā. Mums būs jāpievieno neliela nobīde (bias) šai pārbaudei, lai izvairītos no artefakta, ko sauc par "ēnu pinnēm" (shadow acne), par ko mēs runāsim tālāk.
- Pamatojoties uz salīdzinājumu, noteikt ēnas koeficientu (piem., 1.0 izgaismotam, 0.3 noēnotam).
- Pielietot šo ēnas koeficientu gala krāsas aprēķinam (piem., reizināt apkārtējās un izkliedētās gaismas komponentes ar ēnas koeficientu).
- Renderēt ainu: Uzzīmējiet visus objektus ainā.
3. nodaļa: Biežākās problēmas un risinājumi
Ieviešot pamata ēnu kartēšanu, ātri atklāsies vairāki bieži sastopami vizuālie artefakti. To izpratne un labošana ir būtiska, lai sasniegtu augstas kvalitātes rezultātus.
Ēnu pinnes (pašēnošanās artefakti)
Problēma: Jūs varat redzēt dīvainus, nepareizus tumšu līniju vai muarē līdzīgus rakstus uz virsmām, kurām vajadzētu būt pilnībā izgaismotām. To sauc par "ēnu pinnēm". Tas notiek, jo dziļuma vērtība, kas saglabāta ēnu kartē, un dziļuma vērtība, kas aprēķināta ainas piegājiena laikā, ir paredzētas vienai un tai pašai virsmai. Peldošā komata neprecizitāšu un ēnu kartes ierobežotās izšķirtspējas dēļ nelielas kļūdas var likt fragmentam nepareizi noteikt, ka tas atrodas aiz sevis, izraisot pašēnošanos.
Risinājums: dziļuma nobīde (Depth Bias). Vienkāršākais risinājums ir ieviest nelielu nobīdi `currentDepth` pirms salīdzināšanas. Liekot fragmentam šķist nedaudz tuvāk gaismai, nekā tas patiesībā ir, mēs to "izstumjam" no savas ēnas.
float shadow = currentDepth > depthFromShadowMap + bias ? 0.3 : 1.0;
Pareizās nobīdes vērtības atrašana ir smalks līdzsvara akts. Ja tā ir pārāk maza, pinnes paliek. Ja tā ir pārāk liela, rodas nākamā problēma.
Pītera Pena efekts
Problēma: Šis artefakts, nosaukts pēc tēla, kurš prata lidot un pazaudēja savu ēnu, izpaužas kā redzama sprauga starp objektu un tā ēnu. Tas liek objektiem izskatīties tā, it kā tie lidotu vai būtu atrauti no virsmām, uz kurām tiem vajadzētu atrasties. Tas ir tiešs rezultāts pārāk lielas dziļuma nobīdes izmantošanai.
Risinājums: no slīpuma atkarīga dziļuma nobīde (Slope-Scale Depth Bias). Stabilāks risinājums nekā konstanta nobīde ir padarīt nobīdi atkarīgu no virsmas stāvuma attiecībā pret gaismu. Stāvāki poligoni ir vairāk pakļauti pinnēm un prasa lielāku nobīdi. Plakanākiem poligoniem nepieciešama mazāka nobīde. Lielākā daļa grafikas API, ieskaitot WebGL, nodrošina funkcionalitāti, lai automātiski piemērotu šāda veida nobīdi dziļuma piegājiena laikā, kas parasti ir labāk nekā manuāla nobīde fragmentu ēnotājā.
Perspektīvas antialiass (Robainas malas)
Problēma: Jūsu ēnu malas izskatās kantainas, robainas un pikseļotas. Tā ir antialiasa forma. Tas notiek tāpēc, ka ēnu kartes izšķirtspēja ir ierobežota. Viens pikselis (jeb tekselis) ēnu kartē var aptvert lielu laukumu uz virsmas gala ainā, īpaši virsmām, kas atrodas tuvu kamerai vai tiek skatītas no asa leņķa. Šī izšķirtspējas neatbilstība izraisa raksturīgo kantaino izskatu.
Risinājums: Ēnu kartes izšķirtspējas palielināšana (piem., no 1024x1024 uz 4096x4096) var palīdzēt, bet tas ir saistīts ar ievērojamām atmiņas un veiktspējas izmaksām un pilnībā neatrisina pamatproblēmu. Īstie risinājumi slēpjas progresīvākās tehnikās.
4. nodaļa: Progresīvas ēnu kartēšanas tehnikas
Pamata ēnu kartēšana nodrošina pamatu, bet profesionālās lietojumprogrammās tiek izmantoti sarežģītāki algoritmi, lai pārvarētu tās ierobežojumus, īpaši antialiasu.
Procentuāli tuvākā filtrēšana (PCF)
PCF ir visizplatītākā tehnika ēnu malu mīkstināšanai un antialiasa samazināšanai. Tā vietā, lai ņemtu vienu paraugu no ēnu kartes un pieņemtu bināru lēmumu (ēnā vai neēnā), PCF ņem vairākus paraugus no apgabala ap mērķa koordinātu.
Koncepcija: Katram fragmentam mēs nolasām ēnu karti ne tikai vienreiz, bet gan režģa veidā (piem., 3x3 vai 5x5) ap fragmenta projicēto tekstūras koordinātu. Katram no šiem paraugiem mēs veicam dziļuma salīdzināšanu. Galīgā ēnas vērtība ir visu šo salīdzinājumu vidējā vērtība. Piemēram, ja 4 no 9 paraugiem ir ēnā, fragments būs 4/9 noēnots, radot gludu pusēnu (mīkstu ēnas malu).
Ieviešana: Tas tiek darīts pilnībā fragmentu ēnotājā. Tas ietver ciklu, kas atkārtojas pār nelielu kodolu, nolasot ēnu karti katrā nobīdē un uzkrājot rezultātus. WebGL 2 piedāvā aparatūras atbalstu (`texture` ar `sampler2DShadow`), kas var veikt salīdzināšanu un filtrēšanu efektīvāk.
Ieguvums: Krasi uzlabo ēnu kvalitāti, aizstājot cietas, robainas malas ar gludām, mīkstām malām.
Izmaksas: Veiktspēja samazinās līdz ar paraugu skaitu, kas tiek ņemts katram fragmentam.
Kaskādes ēnu kartes (CSM)
CSM ir industrijas standarta risinājums ēnu renderēšanai no viena virziena gaismas avota (piemēram, saules) ļoti lielā ainā. Tā tieši risina perspektīvas antialiasa problēmu.
Koncepcija: Galvenā ideja ir tāda, ka objektiem, kas atrodas tuvu kamerai, ir nepieciešama daudz augstāka ēnu izšķirtspēja nekā objektiem, kas atrodas tālu. CSM sadala kameras skata noplūdes piramīdu vairākās sadaļās jeb "kaskādēs" gar tās dziļumu. Pēc tam katrai kaskādei tiek renderēta atsevišķa, augstas kvalitātes ēnu karte. Kaskāde, kas ir vistuvāk kamerai, aptver nelielu pasaules telpas apgabalu un tādējādi tai ir ļoti augsta efektīvā izšķirtspēja. Tālākās kaskādes aptver arvien lielākus apgabalus ar tādu pašu tekstūras izmēru, kas ir pieņemami, jo šīs detaļas spēlētājam ir mazāk redzamas.
Ieviešana: Tā ir ievērojami sarežģītāka.
- CPU sadaliet kameras noplūdes piramīdu 2-4 kaskādēs.
- Katrā kaskādē aprēķiniet cieši pieguļošu ortogrāfiskās projekcijas matricu gaismai, kas perfekti ietver šo noplūdes piramīdas daļu.
- Renderēšanas ciklā veiciet dziļuma piegājienu vairākas reizes — vienu reizi katrai kaskādei, renderējot uz citu ēnu karti (vai tekstūru atlasa reģionu).
- Gala ainas piegājiena fragmentu ēnotājā nosakiet, kurai kaskādei pieder pašreizējais fragments, pamatojoties uz tā attālumu no kameras.
- Nolasiet atbilstošās kaskādes ēnu karti, lai aprēķinātu ēnu.
Ieguvums: Nodrošina nemainīgi augstas izšķirtspējas ēnas lielos attālumos, padarot to ideāli piemērotu āra vidēm.
Variances ēnu kartes (VSM)
VSM ir vēl viena tehnika mīksto ēnu veidošanai, bet tā izmanto atšķirīgu pieeju nekā PCF.
Koncepcija: Tā vietā, lai ēnu kartē glabātu tikai dziļumu, VSM glabā divas vērtības: dziļumu (pirmo momentu) un dziļumu kvadrātā (otro momentu). Šīs divas vērtības ļauj mums aprēķināt dziļuma sadalījuma varianci. Izmantojot matemātisku rīku, ko sauc par Čebiševa nevienādību, mēs varam novērtēt varbūtību, ka fragments atrodas ēnā. Galvenā priekšrocība ir tā, ka VSM tekstūru var izpludināt, izmantojot standarta aparatūras paātrinātu lineāro filtrēšanu un mipmapping, kas ir matemātiski nederīgi standarta dziļuma kartei. Tas ļauj iegūt ļoti lielas, mīkstas un gludas ēnu pusēnas ar fiksētām veiktspējas izmaksām.
Trūkums: VSM galvenais trūkums ir "gaismas noplūde", kad gaisma var šķistami izspiesties cauri objektiem situācijās ar pārklājošiem aizsegiem, jo statistiskā aproksimācija var sabrukt.
5. nodaļa: Praktiski ieviešanas padomi un veiktspēja
Ēnu kartes izšķirtspējas izvēle
Jūsu ēnu kartes izšķirtspēja ir tiešs kompromiss starp kvalitāti un veiktspēju. Lielāka tekstūra nodrošina asākas ēnas, bet patērē vairāk video atmiņas un prasa ilgāku laiku renderēšanai un nolasīšanai. Biežākie izmēri ietver:
- 1024x1024: Labs sākumpunkts daudzām lietojumprogrammām.
- 2048x2048: Piedāvā manāmu kvalitātes uzlabojumu darbvirsmas lietojumprogrammām.
- 4096x4096: Augsta kvalitāte, bieži izmantota galvenajiem resursiem (hero assets) vai dzinējos ar robustu atsijāšanu (culling).
Gaismas noplūdes piramīdas optimizēšana
Lai maksimāli izmantotu katru pikseli jūsu ēnu kartē, ir ļoti svarīgi, lai gaismas projekcijas apjoms (tā ortogrāfiskā kaste vai perspektīvā noplūdes piramīda) būtu pēc iespējas ciešāk pielāgots ainas elementiem, kuriem nepieciešamas ēnas. Virziena gaismai tas nozīmē tās ortogrāfiskās projekcijas pielāgošanu, lai ietvertu tikai redzamo kameras noplūdes piramīdas daļu. Jebkura neizmantota vieta ēnu kartē ir izšķiesta izšķirtspēja.
WebGL paplašinājumi un versijas
WebGL 1 pret WebGL 2: Lai gan ēnu kartēšana ir iespējama WebGL 1, tā ir daudz vieglāka un efektīvāka WebGL 2. WebGL 1 ir nepieciešams `WEBGL_depth_texture` paplašinājums, lai izveidotu dziļuma tekstūru. WebGL 2 šī funkcionalitāte ir iebūvēta. Turklāt WebGL 2 nodrošina piekļuvi ēnu nolasītājiem (`sampler2DShadow`), kas var veikt aparatūras paātrinātu PCF, piedāvājot ievērojamu veiktspējas pieaugumu salīdzinājumā ar manuāliem PCF cikliem ēnotājā.
Ēnu atkļūdošana
Ēnas var būt ļoti grūti atkļūdot. Viena no noderīgākajām tehnikām ir vizualizēt ēnu karti. Pagaidu kārtā modificējiet savu lietojumprogrammu, lai renderētu dziļuma tekstūru no konkrēta gaismas avota tieši uz četrstūra ekrānā. Tas ļauj jums precīzi redzēt, ko "redz" gaisma. Tas var nekavējoties atklāt problēmas ar jūsu gaismas matricām, noplūdes piramīdas atsijāšanu vai objektu renderēšanu dziļuma piegājiena laikā.
Noslēgums
Reāllaika ēnu kartēšana ir mūsdienu 3D grafikas stūrakmens, kas pārveido plakanas, nedzīvas ainas par ticamām un dinamiskām pasaulēm. Lai gan koncepcija par renderēšanu no gaismas perspektīvas ir vienkārša, augstas kvalitātes rezultātu sasniegšanai bez artefaktiem ir nepieciešama dziļa izpratne par pamatā esošo mehāniku, sākot no divu piegājienu konveijera līdz dziļuma nobīdes un antialiasa niansēm.
Sākot ar pamata ieviešanu, jūs varat pakāpeniski risināt biežākos artefaktus, piemēram, ēnu pinnes un robainas malas. No turienes jūs varat uzlabot savus vizuālos efektus ar progresīvām tehnikām, piemēram, PCF mīkstām ēnām vai kaskādes ēnu kartēm liela mēroga vidēm. Ceļojums ēnu renderēšanā ir lielisks piemērs mākslas un zinātnes apvienojumam, kas padara datorgrafiku tik saistošu. Mēs aicinām jūs eksperimentēt ar šīm tehnikām, pārbaudīt to robežas un ienest jaunu reālisma līmeni savos WebGL projektos.